﻿using System.Collections;
using System.Collections.Generic;
using HK.Core.Common;
using HK.Editor.GUIEx.Protocol.Selection;
using UnityEditor;
using UnityEngine;

namespace HK.Editor.GUIEx.Selection
{
    /// <summary>
    /// 列表选中信息
    /// </summary>
    public class ListSelection : ClassExtension, IListSelection
    {

#region Create

        /// <summary>
        /// 创建列表选中信息
        /// </summary>
        /// <param name="iOnSelectionChanged">选中变更回调</param>
        /// <returns>列表选中信息</returns>
        public static IListSelection Create(ListSelectionChanged iOnSelectionChanged)
        {
            return new ListSelection(iOnSelectionChanged);
        }

#endregion
        
        /// <summary>
        /// 索引列表
        /// </summary>
        private List<int> _indexes = null;

        /// <summary>
        /// 首次选中
        /// </summary>
        internal int? firstSelected;
		
        /// <summary>
        /// 第一个索引
        /// <para>-1 : 无效索引</para>
        /// </summary>
        public int First => _indexes.Count > 0 ? _indexes[0] : -1;
		
        /// <summary>
        /// 最后一个索引
        /// <para>-1 : 无效索引</para>
        /// </summary>
        public int Last => _indexes.Count > 0 ? _indexes[_indexes.Count - 1] : -1;

        /// <summary>
        /// 长度
        /// </summary>
        public int Length => _indexes.Count;
        
        /// <summary>
        /// 运算符重载 : []
        /// </summary>
        /// <param name="iIndex">索引</param>
        public int this[int iIndex] 
        {

            get => _indexes[iIndex];
            set {

                var oldIndex = _indexes[iIndex];
                _indexes[iIndex] = value;
				
                _selectionChanged?.Invoke(value, true);

                if (oldIndex == firstSelected) 
                {
                    firstSelected = value;
                }
            }
        }

        /// <summary>
        /// 选中变更世间
        /// </summary>
        private ListSelectionChanged _selectionChanged = null;
        
        /// <summary>
        /// 保护构造函数
        /// </summary>
        /// <param name="iOnSelectionChanged">选中变更事件</param>
        protected ListSelection(ListSelectionChanged iOnSelectionChanged) 
        {
            _indexes = new List<int>();
            _selectionChanged = iOnSelectionChanged;
        }
        
        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="iIndexes">索引列表</param>
        /// <param name="iOnSelectionChanged">选中变更事件</param>
        protected ListSelection(int[] iIndexes, ListSelectionChanged iOnSelectionChanged) 
        {
            _indexes = new List<int>(iIndexes);
            _selectionChanged = iOnSelectionChanged;
        }
        
        /// <summary>
        /// 校验是否包含指定索引
        /// </summary>
        /// <param name="iIndex">索引</param>
        /// <returns>true:包含; false:不包含;</returns>
        public bool Contains(int iIndex) => _indexes.Contains(iIndex);
        
        /// <summary>
        /// 追加选中索引
        /// </summary>
        /// <param name="iIndex">索引</param>
        private void Append(int iIndex) 
        {
            if (iIndex >= 0 && !Contains(iIndex)) 
            {
                _indexes.Add(iIndex);
                _selectionChanged?.Invoke(iIndex, true);
            }
        }
        
        /// <summary>
        /// 添加范围
        /// </summary>
        /// <param name="iFrom">开始索引</param>
        /// <param name="iTo">目标索引</param>
        private void AppendRange(int iFrom, int iTo) 
        {
            // to > from : 1
            // to == from : 0
            // to < from : -1
            var dir = (int)Mathf.Sign(iTo - iFrom);
            if (0 != dir) 
            {
                for (var i = iFrom; i != iTo; i += dir) 
                {
                    Append(i);
                }
            }
            Append(iTo);
        }
        
        /// <summary>
        /// 移除索引
        /// </summary>
        /// <param name="iIndex">索引</param>
        public void Remove(int iIndex) 
        {
            if (Contains(iIndex)) 
            {
                _indexes.Remove(iIndex);
                _selectionChanged?.Invoke(iIndex, false);
            }
        }
        
        /// <summary>
        /// 清空
        /// </summary>
        /// <param name="iClearFirst"></param>
        /// <param name="iChangedCallBack">调用变更回调标志位</param>
        public void Clear(bool iClearFirst = false, bool iChangedCallBack = true)
        {
            var count = _indexes.Count;
            if (0 < count)
            {
                for (var idx = 0; idx < count; ++idx)
                {
                    var index = _indexes[idx];
                    if (iChangedCallBack)
                    {
                        _selectionChanged?.Invoke(index, false);
                    }
                }
            }
            _indexes.Clear();

            if (iClearFirst)
            {
                firstSelected = null;
            }
        }
        
        /// <summary>
        /// 克隆
        /// </summary>
        /// <returns>列表选中信息</returns>
        public IListSelection Clone() 
        {
            return new ListSelection(ToArray(), _selectionChanged)
            {
                firstSelected = firstSelected
            };
        }
        
        /// <summary>
        /// 还原值
        /// </summary>
        /// <param name="iData">数据</param>
        public void RevertValues(object iData) 
        {
            if(!(iData is SerializedProperty list)) return;
            for (var i = 0; i < Length; i++) 
            {
                var property = list.GetArrayElementAtIndex(this[i]);
                if (property.isInstantiatedPrefab) 
                {
                    property.prefabOverride = false;
                }
            }

            list.serializedObject.ApplyModifiedProperties();
            list.serializedObject.Update();

            HandleUtility.Repaint();
        }

        /// <summary>
        /// 复制
        /// </summary>
        /// <param name="iList">List</param>
        public void Duplicate(SerializedProperty iList) 
        {
            var offset = 0;
            for (var i = 0; i < Length; i++) {

                this[i] += offset;

                iList.GetArrayElementAtIndex(this[i]).DuplicateCommand();
                iList.serializedObject.ApplyModifiedProperties();
                iList.serializedObject.Update();

                offset++;
            }

            HandleUtility.Repaint();
        }

        /// <summary>
        /// 删除
        /// </summary>
        /// <param name="iList">List</param>
        public void Delete(SerializedProperty iList) 
        {
            // 排序
            Sort();
			
            var i = Length;
            while (--i > -1) 
            {
                iList.GetArrayElementAtIndex(this[i]).DeleteCommand();
            }

            // 清空索引
            Clear();

            // 应用变更
            iList.serializedObject.ApplyModifiedProperties();
            iList.serializedObject.Update();

            // 重画
            HandleUtility.Repaint();
        }
        
        /// <summary>
        /// 转换成Array
        /// </summary>
        /// <returns>索引列表</returns>
        public int[] ToArray() 
        {
            return _indexes.ToArray();
        }
        
#region Event

        /// <summary>
        /// 选中指定的索引
        /// </summary>
        /// <param name="iIndex">索引</param>
        /// <param name="iChangedCallBack">变化回调</param>
        /// <returns>true:有变化; false:无变化;</returns>
        public bool Select(int iIndex, bool iChangedCallBack = true)
        {
            var lastSelected = First;
            Clear(false, iChangedCallBack);
			                
            _indexes.Add(iIndex);
            firstSelected = iIndex;

            var dirty = First != lastSelected;
            if (dirty && iChangedCallBack)
            {
                _selectionChanged?.Invoke(iIndex, true);
            }
            return dirty;
        }
        
        /// <summary>
        /// 排序
        /// </summary>
        public void Sort() 
        {
            if (_indexes.Count > 0) 
            {
                _indexes.Sort();
            }
        }

        /// <summary>
        /// 排序
        /// </summary>
        /// <param name="iComparer">比较器</param>
        public void Sort(System.Comparison<int> iComparer) 
        {
            if (_indexes.Count > 0) {

                _indexes.Sort(iComparer);
            }
        }
        
        /// <summary>
        /// 去除最小索引和最大索引之外的索引
        /// </summary>
        /// <param name="iMinIndex">最小索引</param>
        /// <param name="iMaxIndex">最大索引</param>
        public void Trim(int iMinIndex, int iMaxIndex) 
        {

            var i = _indexes.Count;
            // 从后往前遍历
            while (--i > -1) 
            {
                var index = _indexes[i];
                if (index < iMinIndex || index >= iMaxIndex) 
                {
                    // 若当前选中的索引，则指向当前索引
                    if (index == firstSelected && i > 0) 
                    {
                        firstSelected = _indexes[i - 1];
                    }

                    _indexes.RemoveAt(i);
                    _selectionChanged?.Invoke(index, false);
                }
            }
        }

        /// <summary>
        /// 校验能否还原
        /// </summary>
        /// <param name="iList">List</param>
        /// <returns>true:可还原; false:不可还原;</returns>
        public bool CanRevert(SerializedProperty iList) 
        {

            if (iList.serializedObject.targetObjects.Length == 1) 
            {
                for (var i = 0; i < Length; i++) 
                {
                    if (iList.GetArrayElementAtIndex(this[i]).isInstantiatedPrefab) 
                    {
                        return true;
                    }
                }
            }
            return false;
        }

#region Keyboard

        /// <summary>
        /// 没有任何动作是的选中操作
        /// <para>* Windows : Ctrl + Shift</para>
        /// <para>* macOs : Command + Shift</para>
        /// </summary>
        /// <param name="iIndex">索引</param>
        /// <param name="iEvent">Event</param>
        public void SelectWhenNoAction(int iIndex, Event iEvent)
        {
            // Windows : Ctrl + Shift
            // macOs : Command + Shift
            if (!EditorGUI.actionKey && !iEvent.shift) 
            {
                Select(iIndex, false);
            }
        }
        
        /// <summary>
        /// Append 索引
        /// <para>* Windows : Ctrl - 选中/反选</para>
        /// <para>* macOs : Command - 选中/反选</para>
        /// </summary>
        /// <param name="iIndex">索引</param>
        /// <param name="iEvent">Event</param>
        public void AppendWithAction(int iIndex, Event iEvent) 
        {
            if (EditorGUI.actionKey) 
            {
                // 当前已选中，则反选
                if (Contains(iIndex)) 
                {
                    Remove(iIndex);
                }
                // 追加选中索引
                else 
                {
                    Append(iIndex);
                    firstSelected = iIndex;
                }
            }
            else if (iEvent.shift && _indexes.Count > 0 && firstSelected.HasValue) 
            {
                Clear();
                AppendRange(firstSelected.Value, iIndex);
            }
            else if (!Contains(iIndex)) 
            {
                Select(iIndex);
            }
        }

#endregion

#endregion
        
        public IEnumerator<int> GetEnumerator() 
        {
            return ((IEnumerable<int>)_indexes).GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator() 
        {
            return ((IEnumerable<int>)_indexes).GetEnumerator();
        }
        
    }
}
